﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Media.Animation;
using Sinacor.Infra.UI.Controls.SmartClient;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using Sinacor.Infra.UI.Settings;
using Sinacor.Infra.UI.Common;
using Sinacor.Infra.Common.Security.Cryptography;
using PSE.Framework.UI.Resources;

namespace Sinacor.Infra.UI.Authentication.Providers
{
    public class SinacorWindowLogin : Sinacor.Infra.UI.Controls.SmartClient.SinacorWindowLogin
	{
		private const string loadingStoryboardName = "AnimationLogin";

        #region Controls
        Grid header = null;

        SinacorTextBox txtLogin = null;
        PasswordBox txtPassword = null;
        SinacorButton btnOk = null;
        SinacorButton btnCancel = null;
        SinacorButton btnClose = null;
        SinacorButton btnChangePassword = null;
        SinacorButton btnForgotPassword = null;
        SinacorLabel txtMessage = null;
        SinacorDataGrid dtgHidden = null;
        //Popup PopUpCapsLock = null; 
        #endregion

        #region Events
        /// <summary>
        /// Evento disparado ao preencher os dados do usuário.
        /// </summary>
        public event EventHandler Filled;
        public event EventHandler ChangePassword;
        public event EventHandler ForgotPassword;
        #endregion

        #region Properties
        /// <summary>
        /// Retorna o Login digitado.
        /// </summary>
        public string LoginName
        {
            get { return txtLogin.Text; }
            set { txtLogin.Text = value; }
        }

        /// <summary>
        /// Retorna a Senha digitada.
        /// </summary>
        public string Password
        {
            get { return RSACryptographer.EncryptString(txtPassword.Password, RSACryptographer.PublicKey); }
        }
        #endregion

        #region Construtor
        /// <summary>
        /// Construtor
        /// </summary>
        public SinacorWindowLogin()
        {
            this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
            this.SetResourceReference(StyleProperty, "ControlWindowLogin");

            SinacorCurrentSettings SinacorCurrentSettings = SinacorCurrentSettings.GetInstance();
            //Verifica se na lista dos módulos instalados consta o Kernel +.
            if (SinacorCurrentSettings.InstalledModules != null)
                foreach (ModuleInformation module in SinacorCurrentSettings.InstalledModules)
                    if (module.Name == "KNL")
                        this.ModulePlus = true;

            this.Loaded += new RoutedEventHandler(SinacorWindowLogin_Loaded);
        }
        #endregion

        #region Tratamentos de eventos

        #region Loaded
        private void SinacorWindowLogin_Loaded(object sender, RoutedEventArgs e)
        {
            dtgHidden.Visibility = Visibility.Collapsed;
        }
        #endregion

        #region ApplyTemplate
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            //Enquanto não for arrumado o problema na borda da janela que aparece quando há o drag,
            //este será desabilitado
            header = this.GetTemplateChild("GridDragMove") as Grid;
            header.MouseLeftButtonDown += new MouseButtonEventHandler(header_MouseLeftButtonDown);

            txtLogin = this.GetTemplateChild("txtLogin") as SinacorTextBox;
            txtPassword = this.GetTemplateChild("txtPassword") as PasswordBox;

            btnOk = this.GetTemplateChild("btnOK") as SinacorButton;
            btnCancel = this.GetTemplateChild("btnCancel") as SinacorButton;
            btnChangePassword = this.GetTemplateChild("btnChangePassword") as SinacorButton;
            btnForgotPassword = this.GetTemplateChild("btnForgotPassword") as SinacorButton;
            txtMessage = this.GetTemplateChild("txtMessage") as SinacorLabel;
            btnClose = this.GetTemplateChild("btnClose") as SinacorButton;

            //PopUpCapsLock = this.GetTemplateChild("Part_PopUpCapsLock") as Popup;
            //PopUpCapsLock.StaysOpen = false;

            btnClose.Click += new RoutedEventHandler(btnClose_Click);
            btnOk.Click += new RoutedEventHandler(btnOk_Click);
            btnCancel.Click += new RoutedEventHandler(btnCancel_Click);
            btnChangePassword.Click += new RoutedEventHandler(btnChangePassword_Click);
            btnForgotPassword.Click += new RoutedEventHandler(btnForgotPassword_Click);
            
            txtPassword.GotFocus += new RoutedEventHandler(txtPassword_GotFocus);
            txtPassword.Password = "";
            txtPassword.PasswordChar = '*';

            // Existe um erro de thread no SinacorDataGrid que ocorre somente na seguinte situação:
            // 1- Abrir a aplicação;
            // 2- A grid deve permitir inserção;
            // 3- Deve existir pelo menos um ContentControl (ex: SinacorLabel) dentro da célula com binding na property Content;
            // 4- O grid deve ser carregado vazio e a janela fechada com ele ainda vazio;
            // Após esses passos, ocorrerá um erro de thread em qualquer outro grid no momento em que o ItemsSource for preenchido.
            // Para resolver o problema foi criado um grid oculto com somente um registro na janela de menu, dessa forma o erro de 
            // thread não ocorre mais.
            dtgHidden = this.GetTemplateChild("dtgHidden") as SinacorDataGrid;
            dtgHidden.ItemsSource = new ModelCollection<ModelHidden>() { new ModelHidden() { Codigo = 1 } };


            if (String.IsNullOrEmpty(LoginName))
                txtLogin.Focus();
            else
                txtPassword.Focus();

            //CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, btnClose_Click));	
        }
        #endregion

        #region header_MouseLeftButtonDown
        void header_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }
        #endregion

        #region txtPassword_GotFocus
        void txtPassword_GotFocus(object sender, RoutedEventArgs e)
        {
            //if(Console.CapsLock)
            //{
            //    PopUpCapsLock.IsOpen = true;
            //}
            //else
            //{
            //    PopUpCapsLock.IsOpen = false;
            //}
        }
        #endregion

        #region btnOk_Click
        /// <summary>
        /// Método disparado no click do botão ok.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOk_Click(object sender, RoutedEventArgs e)
        {
            this.Authenticate();
        }
        #endregion

        #region btnCancel_Click
        /// <summary>
        /// Método disparado no click do botão cancelar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
        #endregion

        #region btnClose_Click
        protected void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        #endregion

        #region btnChangePassword_Click
        private void btnChangePassword_Click(object sender, RoutedEventArgs e)
        {
            txtMessage.Content = "";

            if (String.IsNullOrEmpty(txtLogin.Text.Trim()))
            {
                string message = FindResource("Infra.Application.Message.LoginIsEmpty").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);
                txtLogin.Focus();
            }
            else
            {
                this.SetIsLoading(true);

                // Dispara evento de troca de senha
                if (ChangePassword != null)
                    ChangePassword(this, new EventArgs());

                StartAnimation();
            }
        }
        #endregion

        #region btnForgotPassword_Click
        private void btnForgotPassword_Click(object sender, RoutedEventArgs e)
        {
            txtMessage.Content = "";

            if (String.IsNullOrEmpty(txtLogin.Text.Trim()))
            {
                string message = FindResource("Infra.Application.Message.LoginIsEmpty").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);
                txtLogin.Focus();
            }
            else if (this.ConfirmPasswordReset())
            {
                this.SetIsLoading(true);

                // Dispara evento de esqueci minha senha
                if (ForgotPassword != null)
                    ForgotPassword(this, new EventArgs());

                StartAnimation();
            }
        }
        #endregion

        #endregion

        #region Authenticate
        private void Authenticate()
        {
            txtMessage.Content = "";

            if (String.IsNullOrEmpty(txtLogin.Text.Trim()))
            {
                string message = FindResource("Infra.Application.Message.LoginIsEmpty").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);
                txtLogin.Focus();
            }
            else if (String.IsNullOrEmpty(txtPassword.Password.Trim()))
            {
                string message = FindResource("Infra.Application.Message.PasswordIsEmpty").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);
                txtPassword.Focus();
            }
            else
            {
                this.SetIsLoading(true);

                if (Filled != null)
                    Filled(this, new EventArgs());

                StartAnimation();
            }
        }
        #endregion

        #region ShowInvalidLogin
        /// <summary>
        /// Exibe mensagem de usuário ou senha inválidos
        /// </summary>
        public void ShowInvalidLogin()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowInvalidLogin);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                StopAnimation();
                this.txtPassword.Focus();

                this.txtMessage.Content = (string)ResourceProviderManager.GetResourceProvider().TryGetResource("Infra.Controls.Message.InvalidUserPassword");
            }
        }
        #endregion

        #region ShowInvalidUser
        /// <summary>
        /// Exibe mensagem de usuário inválido
        /// </summary>
        public void ShowInvalidUser()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowInvalidUser);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                StopAnimation();
                this.txtLogin.Focus();

                this.txtMessage.Content = (string)ResourceProviderManager.GetResourceProvider().TryGetResource("Infra.Controls.Message.InvalidUser");
            }
        }
        #endregion

        #region ShowError
        /// <summary>
        /// Exibe mensagem de erro na autenticação
        /// </summary>
        public void ShowError(string message, PSE.Framework.ErrorLogging.LogData logData)
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread == this.Dispatcher.Thread)
            {
                //Para a animação.
                StopAnimation();
                this.txtPassword.Focus();

                SinacorMessageBox.ShowError(message, null, logData, this);
            }
            else
            {
                this.Dispatcher.Invoke(new Action<string, PSE.Framework.ErrorLogging.LogData>(ShowError), message, logData);
            }
        }
        #endregion

        #region CloseWindow
        /// <summary>
        /// Fecha a janela
        /// </summary>
        internal void CloseWindow()
        {
            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(CloseWindow);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                this.Close();
            }
        }
        #endregion

        #region ShowChangePassword
        /// <summary>
        /// Exibe a janela de troca de senha
        /// </summary>
        public void ShowChangePassword()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowChangePassword);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                this.StopAnimation();

                this.ShowChangePasswordWindow(this.LoginName);
            }
        }
        #endregion

		public void ShowBlockedAccount()
		{
			this.SetIsLoading(false);

			if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
			{
				Action dlg = new Action(ShowBlockedAccount);
				this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
			}
			else
			{
				StopAnimation();
				string message = FindResource("Infra.Application.Message.AccountBlocked").ToString();
				SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);
			}
		}

        public void ShowPasswordExpired()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowPasswordExpired);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                StopAnimation();

                string message = FindResource("Infra.Application.Message.PasswordExpired").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Exclamation);

                this.ShowChangePasswordWindow(this.LoginName, txtPassword.Password);
            }
        }

        private void ShowChangePasswordWindow(string login)
        {
            this.ShowChangePasswordWindow(login, null);
        }

        private void ShowChangePasswordWindow(string login, string password)
        {
            SinacorWindowChangePassword changePasswordWindow = (password == null ? new SinacorWindowChangePassword(login) : new SinacorWindowChangePassword(login, password));
            changePasswordWindow.Owner = this;

            // Se a troca de senha for efetuada com sucesso, já efetua login
            if (Convert.ToBoolean(changePasswordWindow.ShowDialog()))
            {
                txtPassword.Password = changePasswordWindow.NewPassword;
                this.Authenticate();
            }
        }

        private bool ConfirmPasswordReset()
        {
            string message = FindResource("Infra.Application.Message.ConfirmPasswordReset").ToString();
            return SinacorMessageBox.Show(message, SinacorMessageBoxButton.YesNo, SinacorMessageBoxImage.Question) == SinacorMessageBoxResult.Yes;
        }

        public void ShowInvalidEmail()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowInvalidEmail);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                StopAnimation();
                this.txtLogin.Focus();

                string message = FindResource("Infra.Application.Message.ResetPasswordInvalidEmail").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
            }
        }

        public void ShowPasswordReset()
        {
            this.SetIsLoading(false);

            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
            {
                Action dlg = new Action(ShowPasswordReset);
                this.Dispatcher.Invoke(DispatcherPriority.Normal, dlg);
            }
            else
            {
                //Para a animação.
                StopAnimation();
                this.txtPassword.Focus();

                string message = FindResource("Infra.Application.Message.PasswordReset").ToString();
                SinacorMessageBox.Show(message, SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Information);
            }
        }

        #region StartAnimation
        /// <summary>
        /// Inicia a animação de loading
        /// </summary>
        private void StartAnimation()
        {
            Storyboard sb = (Storyboard)this.FindResource(loadingStoryboardName);
            sb.Begin(btnOk, true);
        }
        #endregion

        #region StopAnimation
        /// <summary>
        /// Interrompe a animação de loading
        /// </summary>
        private void StopAnimation()
        {
            Storyboard sb = (Storyboard)this.FindResource(loadingStoryboardName);
            sb.Stop(btnOk);
        }
        #endregion

        #region SetIsLoading
        /// <summary>
        /// Altera a propriedade IsLoading quando a tela de login está processando o login ou não.
        /// </summary>
        /// <param name="isLoadingStatus"></param>
        private void SetIsLoading(bool isLoadingStatus)
        {
            if (System.Threading.Thread.CurrentThread != this.Dispatcher.Thread)
                this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<bool>(SetIsLoading), isLoadingStatus);
            else
                this.SetValue(Sinacor.Infra.UI.Controls.SmartClient.SinacorWindowLogin.IsLoadingProperty, isLoadingStatus);
        }
        #endregion

        #region DisableLoginName
        /// <summary>
        /// Desabilita a entrada do Login
        /// </summary>
        internal void DisableLoginName()
        {
            this.txtLogin.IsEnabled = false;
        }
        #endregion
    }

    /// <summary>
    /// Model utilizada no grid oculto (ver OnApplyTemplate)
    /// </summary>
    internal class ModelHidden : BaseModel
    {
        public int Codigo { get; set; }
    }
}
